﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Tao.FreeType;
using System.Runtime.InteropServices;

namespace Block.Helper
{
    public class Character
    {
        public Texture2D Texture { get; set; }
        public int OffsetX { get; set; }
        public int OffserY { get; set; }
    }
    public class FTFont
    {
        private uint font_size;

        private FT_FaceRec face;
        private IntPtr faceptr;
        private GraphicsDevice gd;
        private Character baseCharacter;
        private int yoffset;

        private Dictionary<char, Character> buffer = new Dictionary<char, Character>();
        public FTFont(GraphicsDevice graphicsdevice, string font, uint size,int globalyoffset)
        {
            font_size = size;
            gd = graphicsdevice;
            yoffset = globalyoffset;
            IntPtr libptr;
            int ret = FT.FT_Init_FreeType(out libptr);
            if (ret != 0) return;

            int retb = FT.FT_New_Face(libptr, font, 0, out faceptr);
            if (retb != 0) return;

            face = (FT_FaceRec)Marshal.PtrToStructure(faceptr, typeof(FT_FaceRec));
            FT.FT_Set_Char_Size(faceptr, (int)size << 6, (int)size << 6, 96, 96);
            FT.FT_Set_Pixel_Sizes(faceptr, size, size);
            baseCharacter = CreateChar('i');
        }

        private FT_GlyphSlotRec GetCharBitmap(uint c)
        {

            uint index = FT.FT_Get_Char_Index(faceptr, c);

            int ret = FT.FT_Load_Glyph(faceptr, index, FT.FT_LOAD_DEFAULT);

            FT_GlyphSlotRec glyph_rec = (FT_GlyphSlotRec)Marshal.PtrToStructure(face.glyph, typeof(FT_GlyphSlotRec));
            int retb = FT.FT_Render_Glyph(ref glyph_rec, FT_Render_Mode.FT_RENDER_MODE_NORMAL);
            return glyph_rec;
        }
        private Character CreateChar(char c)
        {
            Character ch = new Character();
            if (buffer.ContainsKey(c))
            {
                return buffer[c];
            }
            if (c != ' ')
            {
                var tt = GetCharBitmap(Convert.ToUInt32(c));
                var charoffsety = (face.ascender >> 6) - tt.bitmap_top;
                var charoffsetx = tt.bitmap_left;

                byte[] bmp = new byte[tt.bitmap.rows * tt.bitmap.width];
                Marshal.Copy(tt.bitmap.buffer, bmp, 0, bmp.Length);

                Color[] colordata = new Color[tt.bitmap.rows * tt.bitmap.width];
                for (int i = 0; i < colordata.Length; i++)
                {
                    colordata[i] = new Color(255, 255, 255, bmp[i]);
                }

                Texture2D texture = new Texture2D(gd, tt.bitmap.width, tt.bitmap.rows);
                texture.SetData<Color>(colordata);
                PreMultiplyAlphas(texture);
                ch.Texture = texture;
                ch.OffserY = charoffsety;
                ch.OffsetX = charoffsetx;
                buffer.Add(c, ch);
            }
            else
            {
                Texture2D texture = new Texture2D(gd, baseCharacter.Texture.Width, 1);
                ch.Texture = texture;
                buffer.Add(c, ch);
            }
            return ch;
        }
        private static void PreMultiplyAlphas(Texture2D ret)
        {
            Byte4[] data = new Byte4[ret.Width * ret.Height];
            ret.GetData<Byte4>(data);
            for (int i = 0; i < data.Length; i++)
            {
                Vector4 vec = data[i].ToVector4();
                float alpha = vec.W / 255.0f;
                int a = (int)(vec.W);
                int r = (int)(alpha * vec.X);
                int g = (int)(alpha * vec.Y);
                int b = (int)(alpha * vec.Z);
                uint packed = (uint)(
                    (a << 24) +
                    (b << 16) +
                    (g << 8) +
                    r
                    );

                data[i].PackedValue = packed;
            }
            ret.SetData<Byte4>(data);
        }

        private Character[] CreateStringTexture(string s)
        {
            var temp = new Character[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                temp[i] = CreateChar(s[i]);
            }
            return temp;
        }
        private void DrawText(SpriteBatch sb, Vector2 pos, Character[] text, Color color)
        {
            int presentx = (int)pos.X;
            for (int i = 0; i < text.Length; i++)
            {
                sb.Draw(text[i].Texture, new Rectangle(presentx + text[i].OffsetX, (int)pos.Y + text[i].OffserY+yoffset, text[i].Texture.Width, text[i].Texture.Height), color);
                presentx += (1 + (int)text[i].Texture.Width + text[i].OffsetX);
            }
        }
        public void DrawText(SpriteBatch sb, Vector2 pos, string text, Color color)
        {
            var tt = CreateStringTexture(text);
            DrawText(sb, pos, tt, color);
        }
    }
}
